﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cloo;
using Processing;
using System.Drawing;
using Processing.Structures;

namespace RayTracer.Kernels
{
    /// <summary>
    /// Kamerainitialisierung: Durch diese Kernel werden die ersten Strahlen berechnet.
    /// Diese Kernel dient dazu von vorn herein nur wenige Daten an die Grafikkarte 
    /// übertragen zu müssen und die Masse an Daten auf der Grafikkarte erst zu erschaffen.
    /// </summary>
    partial class KernelInitCamera : AKernel
    {
        #region Attribute
        #region CPU
        private Int2 cpu_resolution;
        private float cpu_focus;
        private Float4 cpu_pos;
        private Float4 cpu_xAxis;
        private Float4 cpu_yAxis;
        private Float4 cpu_zAxis;
        #endregion CPU

        #region GPU
        private Buffer<Float4> gpu_rayPos;
        private Buffer<Float4> gpu_rayDir;
        #endregion GPU
        #endregion Attribute

        #region Eigenschaften
        #region Dateninitialisierung
        /// <summary>
        /// Gibt und setzt die Auflösung des Bildbereiches.
        /// </summary>
        public Int2 ScreenResolution { get { return cpu_resolution; } set { cpu_resolution = value; } }

        /// <summary>
        /// Gibt und setzt den Fokusfaktor.
        /// </summary>
        public float CameraFocus { get { return cpu_focus; } set { cpu_focus = value; } }

        /// <summary>
        /// Gibt und setzt die Kameraposition.
        /// </summary>
        public Float4 CameraPosition { get { return cpu_pos; } set { cpu_pos = value; } }

        /// <summary>
        /// Gibt und setzt die Blickwinkel X-Achse.
        /// </summary>
        public Float4 FovXAxis { get { return cpu_xAxis; } set { cpu_xAxis = value; } }

        /// <summary>
        /// Gibt und setzt die Blickwinkel Y-Achse.
        /// </summary>
        public Float4 FovYAxis { get { return cpu_yAxis; } set { cpu_yAxis = value; } }

        /// <summary>
        /// Gibt und setzt die Blickwinkel Z-Achse. 
        /// </summary>
        public Float4 FovZAxis { get { return cpu_zAxis; } set { cpu_zAxis = value; } }

        /// <summary>
        /// Gibt die Positionen der Strahlen an.
        /// </summary>
        public Buffer<Float4> RayPositions
        {
            get
            {
                if (State != KernelState.Done) throw new Exception("Die Kernel wurde noch nicht berechnet.");
                return gpu_rayPos;
            }
        }

        /// <summary>
        /// Gibt die Rotationen der Strahlen an.
        /// </summary>
        public Buffer<Float4> RayDicrections
        {
            get
            {
                if (State != KernelState.Done) throw new Exception("Die Kernel wurde noch nicht berechnet.");
                return gpu_rayDir;
            }
        }
        #endregion Dateninitialisierung

        #region Kernelausführung
        /// <summary>
        /// Gibt den Quellcode des Programms an.
        /// </summary>
        protected override string ProgramSource { get { return KERNEL_SOURCE; } }

        /// <summary>
        /// Gibt den Namen des Programms an.
        /// </summary>
        protected override string ProgramName { get { return KERNEL_MAIN; } }

        /// <summary>
        /// Gibt die Dimension des Programms an.
        /// </summary>
        protected override long[] Dimensions { get { return new long[] { cpu_resolution.x * cpu_resolution.y }; } }

        /// <summary>
        /// Gibt an ob die Kerneldaten valid initialisiert sind.
        /// </summary>
        protected override bool IsValidInitialized
        {
            get
            {
                return
                    cpu_resolution.x > 0 && cpu_resolution.y > 0;
            }
        }
        #endregion Kernelausführung

        /// <summary>
        /// Gibt eine hochgegriffene Schätzung über die Anzahl der Bytes pro Pixel/Ray.
        /// </summary>
        public static int BytesPerPixel { get { return (2 * (sizeof(float) * 4)); } }
        #endregion Eigenschaften

        #region Methoden
        /// <summary>
        /// Instanziiert und initialisiert die Kernel.
        /// </summary>
        /// <param name="processor">Der Prozessor.</param>
        public KernelInitCamera(Processor processor) : base(processor) { }

        #region Kernelausführung
        /// <summary>
        /// Führt Aktionen vor der Ausführung der Kernel - mit dem Run-Aufruf aus. (z. B. Datentransport von CPU zu GPU)
        /// </summary>
        protected override void PrepareKernelExecution()
        {
            base.PrepareKernelExecution();
            gpu_rayPos = Processor.AllocateBuffer<Float4>(cpu_resolution.x * cpu_resolution.y);
            gpu_rayDir = Processor.AllocateBuffer<Float4>(cpu_resolution.x * cpu_resolution.y);
        }

        /// <summary>
        /// Setzt die Daten in Kernelargumente fest.
        /// </summary>
        protected override void SetArguments()
        {
            SetArgument(0, cpu_resolution);
            SetArgument(1, cpu_resolution.x / (float)cpu_resolution.y);
            SetArgument(2, cpu_focus);
            SetArgument(3, cpu_pos);
            SetArgument(4, cpu_xAxis);
            SetArgument(5, cpu_yAxis);
            SetArgument(6, cpu_zAxis);

            SetArgument(7, gpu_rayPos, true);
            SetArgument(8, gpu_rayDir);
        }
        #endregion Kernelausführung
        #endregion Methoden
    }
}
